package search

import (
	"log" //MARK 这些这么简单是因为 是标准库(go自带)导入
	"sync"
)

var matchers = make(map[string]Matcher) //MARK 包级变量, 小写就private; 创建一个map  string->Matcher 開始是nil 意思是一個空容器

// Run performs the search logic.
func Run(searchTerm string) {
	// Retrieve the list of feeds to search through. 拿到配置
	feeds, err := RetrieveFeeds() //MARK 声明的时候同时赋值就是  :=
	if err != nil {               //异常处理
		log.Fatal(err)
	}

	//MARK 创建一个通道 用来传递消息; 里面能拿出结果; 需要关闭
	resultsChanel := make(chan *Result)

	// Setup a wait group so we can process all the feeds.
	var waitGroup sync.WaitGroup //MARK 声明类型写后面 直接得到实例(要实例的指针才要new), 这是个 计数信号量

	// Set the number of goroutines we need to wait for while
	// they process the individual feeds.
	waitGroup.Add(len(feeds)) //MARK 值为我要启动的 goroutines数量; 目的是阻塞主线程到都干完

	// Launch a goroutine for each feed to find the resultsChanel.
	for _, feed := range feeds { //MARK 这里 _ 的位置本来是index 这里用不到;   go有检查,声明了不用会报错
		// Retrieve a matcher for the search. 这是一个多态 各种属性不同
		matcher, exists := matchers[feed.Type] //MARK 也可以用一个变量接,那就少了exists
		if !exists {
			matcher = matchers["default"] //不存在用默认的
		}

		// Launch the goroutine to perform the search.MARK 匿名函数执行
		go func(matcher Matcher, feed *Feed) { //MARK 加*是说这个是指针(一个内存地址,可共享数据), 这里说传指针不说传对象 是因为go是值传递
			Match(matcher, feed, searchTerm, resultsChanel) //会搜索数据源数据,匹配结果到results通道
			waitGroup.Done()                                //MARK 完成工作 计数-1 这里waitGroup不是做完有进的函数, 是闭包形式访问外{}的变量,
			// 每次的匿名函数拿的都是同个waitGroup
		}(matcher, feed) //MARK 这2个每次不同,在外环境马上变了 只能自己用一份,不能闭包用外环境的
	}

	// Launch a goroutine to monitor when all the work is done.
	go func() {
		// Wait for everything to be processed.
		waitGroup.Wait() //MARK 阻塞等到计数0.就是每个任务都完成了

		// Close the channel to signal to the Display
		// function that we can exit the program.
		close(resultsChanel) //MARK 通道最后不收新数据了,需要关闭;这里关闭以后 从通道消费数据的才会罢休
	}() //MARK 另外开携程进行

	// Start displaying resultsChanel as they are available and
	// return after the final result is displayed.
	Display(resultsChanel) //MARK 通道里面的值打印, 这里没数据就会阻塞等待, 直到管道被关闭
}

// Register 各Matcher实现init里面会来注册这边
func Register(feedType string, matcher Matcher) { //MARK matcher参数:这里只要是Matcher接口的实现 不管[值] 还是 [值的指针]
	if _, exists := matchers[feedType]; exists {
		log.Fatalln(feedType, "Matcher already registered")
	}

	log.Println("Register", feedType, "matcher")
	matchers[feedType] = matcher
}
